/**
 * Project: pws-formula
 * Created Date: Thursday, May 3rd 2018, 8:39:13 pm
 * Author: Thomas.Li
 * E-Mail: leeyinghui@hotmail.com
 * Intro: String operation methods
 * -----
 * Last Modified: Tue Jun 19 2018
 * Modified By: Thomas.Li
 * -----
 * Copyright (c) 2018 pareact
 * ------------------------------------
 * Always bet on Javascript!
 */

let str;  //temp string 

/**
 * get the dec value from the strings by the hex format
 * @param {number} start 
 * @param {number} length must be even
 * @returns 
 * @memberof AsciiFormula
 */
function ax(start, length) {
  if (length % 2 == 1) {
    throw new RangeError('The value must be even');
  }
  if (length < 0 || length > 16) {
    throw new RangeError('The length must between 1-16');
  }
  let bf = Buffer.from(str.slice(start, start + length), 'hex');
  return bf.readUIntBE(0, length / 2);
}

/**
 * get the pmbus float value
 * 
 * @param {number} start 
 * @returns 
 * @memberof AsciiFormula
 */
function ap(start) {
  if (start < 0 || start > (str.length - 8)) {
    throw new RangeError('Out of the range');
  }
  let result = [...Array(4).keys()].map(i => parseInt(str.slice(start + i * 2, start + (i + 1) * 2), 16).toString(2).padStart(8, '0'));
  result = result.reverse().join('');
  let reg = /^[0-1]*$/;
  if (!reg.test(result)) throw new TypeError('The value is invalid');
  let mark = result[0] == '1' ? -1 : 1;
  let eMark = parseInt(result.slice(1, 9), 2);
  let mMark = parseInt(result.slice(9), 2);
  let r1 = 2 ** (eMark - 127);
  let r2 = mark * r1 * mMark * (2 ** -23);
  return Math.round((r1 + r2) * 100) / 100;
}

/**
 * get the dec or float value from asccii string
 * 
 * @param {number} start 
 * @param {number} length 
 * @returns 
 * @memberof AsciiFormula
 */
function ad(start, length) {
  if (start < 0 || start > (str.length - length)) {
    throw new RangeError('Out of the range');
  }
  if (length > 16) {
    throw new RangeError('The length must <= 16');
  }
  let result = str.slice(start, start + length);
  let reg1 = /^([+-]?\d+)(\.\d+)?$/;
  if (reg1.test(result)) {
    return parseFloat(result);
  }
  else {
    throw new TypeError('The value is invalid');
  }
}

/**
 * get float value from hex format asccii string 
 * 
 * @param {number} start 
 * @param {number} length 
 * @returns 
 * @memberof AsciiFormula
 */
function ac(start, length) {
  if (length % 2 == 1) {
    throw new RangeError('The value must be even');
  }

  if (start < 0 || start > (str.length - length)) {
    throw new RangeError('Out of the range');
  }
  let bf = Buffer.from(str.slice(start, start + length), 'hex');
  let result = parseFloat(bf.toString());
  if (Number.isNaN(result)) {
    throw new TypeError('Convert to float fault!');
  }
  return result;
}

/**
 * get the float by eval()
 * 
 * @returns 
 * @memberof AsciiFormula
 */
function av() {
  let reg = /^([+-]?\d+)(\.\d+)?$/;
  if (reg.test(str)) {
    return parseFloat(str);
  }
  else {
    throw new TypeError('The value is invalid');
  }
}

/**
 * return the bit value from ascii string (as hex)
 * 
 * @param {number} start 
 * @param {number} index 
 * @returns 
 * @memberof AsciiFormula
 */
function ab(start, index) {
  if (start < 0 || start > (str.length - 2) || index < 0 || index > 7) {
    throw new RangeError('Out of the range');
  }
  let result = str.slice(start, start + 2);
  let reg = /^[0-9a-fA-F]*$/;
  if (reg.test(result)) {
    result = parseInt(str.slice(start, start + 2), 16);
    return (result & (2 ** index)) >>> index;
  }
  else {
    throw new TypeError('The value is invalid');
  }
}

/**
 * Trinocular calculate
 * @param {string} condition 
 * @param {string} a 
 * @param {string} b 
 * @returns 
 */
function pif(condition, a, b) {
  return eval(condition) ? eval(a) : eval(b);
}

/**
 * outer method to call the inner formula by eval(str)
 * @param {string} st 
 * @param {string} formulas 
 * @returns 
 */
function getStringValue(st, formulas) {
  str = st;
  return formulas.map(x => {
    try {
      let result = eval(x);
      return { 'success': true, 'data': result };
    }
    catch (e) {
      return { 'success': false, 'data': e };
    }
  });
}

/**
 * check the string is valid by sort (lrc, sum, pmbus)
 * attention: there is no prefix and endchar
 * @param {string} st 
 * @param {number} sort 
 * @returns 
 */
function verifyString(st, sort) {
  str = st;
  //remove not hex string
  let reg2 = /[^0-9a-fA-F]/gi;
  str = str.replace(reg2, '');
  
  if (str.length < 3) {
    return { 'success': false, 'data': 'The string is too short!' };
  }
  // if (str.length % 2 == 1) {
  //   return { 'success': false, 'data': 'The value is invalid' };
  // }
  let reg = /^[0-9a-fA-F]*$/;

  if (!reg.test(str)) {
    return { 'success': false, 'data': 'The string has invalid character!' };
  }
  try {
    //lrc
    if (sort == 2) {
      let result = Buffer.from(str, 'hex');
      let total = result.reduce((a, b) => a + b);
      return { 'success': total % 256 == 0, 'data': `${total}` };
    }
    //sum
    else if (sort == 3) {
      let result = Buffer.from(str, 'hex');
      let last = result.readUInt8(result.length - 1);
      let total = result.reduce((a, b) => a + b) - last;
      return { 'success': total % 256 == last, 'data': `${total}-${last}` };
    }
    //pmbus
    else if (sort == 4) {
      let result = Buffer.from(str.slice(0, -4));
      let checksum = parseInt(str.slice(-4), 16);
      let total = result.reduce((a, b) => a + b);
      return { 'success': (total + checksum) % 65536 == 0, 'data': `${total}- ${checksum}` };
    }
    else if (sort == 0) {
      return { 'success': true, 'data': `no need to valid data` };
    }
    else {
      return { 'success': false, 'data': 'Sort value is invalid!' };
    }
  }
  catch (e) {
    return { 'success': false, 'data': e };
  }
}

/**
 * get the fully string by check type(lrc, sum, pmbus)
 * @param {string} st 
 * @param {number} sort 
 * @returns 
 */
function getFullString(st, sort) {
  str = st;
  if (str.length < 3) {
    return { 'success': false, 'data': 'The stringF is too short!' };
  }
  if (str.length % 2 == 1) {
    return { 'success': false, 'data': 'The value is invalid' };
  }
  let reg = /^[0-9a-fA-F]*$/;
  if (!reg.test(str)) {
    return { 'success': false, 'data': 'The string has invalid character!' };
  }
  try {
    if (sort == 2) {
      let result = Buffer.from(str, 'hex');
      let total = 256 - (result.reduce((a, b) => a + b) % 256);
      return { 'success': true, 'data': str + total.toString(16).toUpperCase() };
    }
    else if (sort == 3) {
      let result = Buffer.from(str, 'hex');
      let total = result.reduce((a, b) => a + b) % 256;
      return { 'success': true, 'data': str + total.toString(16).toUpperCase() };
    }
    else if (sort == 4) {
      let result = Buffer.from(str);
      let total = 65536 - (result.reduce((a, b) => a + b) % 65536);
      return { 'success': true, 'data': str + total.toString(16).toUpperCase() };
    }
    else {
      return { 'success': false, 'data': 'Sort value is invalid!' };
    }
  }
  catch (e) {
    return { 'success': false, 'data': e };
  }
}

module.exports = {
  getStringValue: getStringValue,
  verifyString: verifyString,
  getFullString: getFullString,
}

